package at.ac.uniklu.rallyemaster.activities;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;


import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import at.ac.uniklu.rallyemaster.R;
import at.ac.uniklu.rallyemaster.RallyeMaster;
import at.ac.uniklu.rallyemaster.TrackingMapOverlay;
import at.ac.uniklu.rallyemaster.adapters.DBTrack;
import at.ac.uniklu.rallyemaster.adapters.DBTrackingPoint;
import at.ac.uniklu.rallyemaster.adapters.RallyeDBAdapter;
import at.ac.uniklu.rallyemaster.domain.TrackingPoint;

public class TrackingActivity extends MapActivity implements OnSharedPreferenceChangeListener{

	// Options Menu
	static final private int SHOW_TRACK = Menu.FIRST; 
	static final private int NEW_ITEM = Menu.FIRST +1; 
	static final private int OPTIONS_ITEM = Menu.FIRST +2;
	
	//Preferences
	private String PREF_DISTANCE ="PREF_DISTANCE";
	private SharedPreferences preferences;
	
	private MapView mapView;
	private AssetManager asset;
	private LinearLayout startButtonLayout;
	private LinearLayout spButtonLayout;
	
	private Button pauseButton;
	private Button startButton;
	
	private List<Overlay> overlays;
	private TrackingMapOverlay trackingMapOverlay; 
	private MyLocationOverlay currentLocationOverlay;
	private GeoPoint geoPoint;
	private OverlayItem overlayItem;
	
	private Location mCurrentBestLocation;
    private LocationListener mLocationListener;
    private LocationManager mLocationManager;
    private float trackingDistance;
    
    private LinkedList<TrackingPoint> trackingPoints;
    private TrackingPoint trackingPoint;
    private TrackingPoint lastTrackingPoint;
    private Location lastTrackingLocation;
    private double gpsDistance;
    private double distance;
    private long timeDistance;
    private double speed;
    private double avgSpeed;
    private Date startTime;
    private Date lastTrackingTime;
    private Date currentTime;
    private Date time;
    private int trackId;
    
    //Databases
    private DBTrackingPoint dbTrackingPoint;
    private DBTrack dbTrack;
    
    RallyeDBAdapter rDBAdapter; //ToDo: USE :-)
    Cursor rDBCursor;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.map_layout);
		
		//getting Preferences
		preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		preferences.registerOnSharedPreferenceChangeListener(this);
		
		trackingDistance= preferences.getInt(PREF_DISTANCE, 10);
		RallyeMaster.logDebug("TrackingDistance: " +  trackingDistance);
		
		//Mp View settings
		mapView = (MapView) findViewById(R.id.mapview);
		mapView.setBuiltInZoomControls(true);// makes the map able to zoom
		mapView.setClickable(true);
		mapView.getController().setZoom(15); // 1 ... widest zoom, 21 tightest zoom
		asset = mapView.getResources().getAssets();
		
		// open database
		dbTrackingPoint = new DBTrackingPoint();
		dbTrack = new DBTrack();
		// get intent extras
		Bundle b = getIntent().getExtras();
		trackId = b.getInt("LastInsertID");
			
		trackingPoints = new LinkedList<TrackingPoint>();
		trackingPoint = new TrackingPoint();
		lastTrackingLocation = new Location ("End");
		gpsDistance = 0;
		distance = 0;
		timeDistance = 0;
		speed = 0;
		avgSpeed = 0;
		time = new Date(0);
		
		//Overlay for the Users current Position
		currentLocationOverlay = new MyLocationOverlay(this, mapView); 
		currentLocationOverlay.enableMyLocation();
		
		//Overlay for showing the current speed and all tracked points
		trackingMapOverlay = new TrackingMapOverlay(mapView.getResources().getDrawable(R.drawable.marker_green), trackingPoints, mapView, asset);
	
        overlays = mapView.getOverlays();
		overlays.add(currentLocationOverlay);
		overlays.add(trackingMapOverlay);
		
		lastTrackingTime = new Date();
		currentTime = new Date();
		
		 //layout for the start button
        startButtonLayout = (LinearLayout) findViewById(R.id.startButtonLayout);
        //layout for the stop and pause button
        spButtonLayout = (LinearLayout) findViewById(R.id.stoppauseButtonLayout);
        
		startButtonLayout.setVisibility(LinearLayout.GONE); // makes the start button invisible
		spButtonLayout.setVisibility(LinearLayout.VISIBLE); // makes the stop an the pause button visible

		// start getting location updates
		trackingDistance = 20;
		mLocationListener = new LocationListener() {

            public void onStatusChanged(String provider, int status, Bundle extras) {
                // Ignore.
            }
            
            public void onProviderEnabled(String provider) {
                // Ignore.
            }
            
            public void onProviderDisabled(String provider) {
                // Ignore.
            }
            
            public void onLocationChanged(Location location) {
                        	
                // calculates the current Time
            	currentTime = new Date();
            	RallyeMaster.logDebug("---------NEW TRACKING POINT----------");
                RallyeMaster.logDebug("Current time: "+ currentTime.getHours() +":" +currentTime.getMinutes() +":"+ currentTime.getSeconds());
        		RallyeMaster.logDebug("Last Tracking Time: "+ lastTrackingTime.getHours() +":" +lastTrackingTime.getMinutes() +":"+ lastTrackingTime.getSeconds());
        		
        		// Figure out if this is a better location
                // If so, use it as the current best location
        		if (isBetterLocation(location, mCurrentBestLocation)) {
                    mCurrentBestLocation = location;
                    
                    if(trackingPoints.size()>0){
                    	
                    	lastTrackingPoint = trackingPoints.getLast(); // get the last trackingPoint;
                    	lastTrackingLocation.setLatitude(lastTrackingPoint.getLatitude());
                    	lastTrackingLocation.setLongitude(lastTrackingPoint.getLongitude());
                    	gpsDistance = mCurrentBestLocation.distanceTo(lastTrackingLocation); // calculates the distance between this two points in meter
                    	distance =+ gpsDistance;
                    	timeDistance = getDuration(lastTrackingTime,currentTime);// time distance in ms
                    	speed = (gpsDistance/1000.0)/(timeDistance/(1000.0*60.0*60.0)); // km/h
                    	avgSpeed = (speed+lastTrackingPoint.getCurrentSpeed())/2;
                    	time = getTrackingTime(getDuration(startTime,currentTime));
                    	
                    }else{
                    	startTime = new Date();
                    	gpsDistance = 0; 
                    	timeDistance = 0;
                    	speed = 0;
                    	avgSpeed = 0;
                    	time = getTrackingTime(0);
                    	
                    }
        
                    //RallyeMaster.logDebug("GPSDistance: " + gpsDistance);
                    //RallyeMaster.logDebug("Distance: " + distance);
                    //RallyeMaster.logDebug("Speed: " + speed +" km/h");
                    //RallyeMaster.logDebug("AvgSpedd: " + avgSpeed +" km/h");
                    
                    trackingPoint = new TrackingPoint();
                    trackingPoint.setLatitude(mCurrentBestLocation.getLatitude());
                    trackingPoint.setLongitude(mCurrentBestLocation.getLongitude());
                    trackingPoint.setDistance(distance);
                    trackingPoint.setCurrentSpeed(speed); // current Speed of the user
                    trackingPoint.setAvgSpeed(avgSpeed); // average speed between the current position and the last tracking point
                    trackingPoint.setTime(time);
                    trackingPoint.setTrackId(trackId);
                    RallyeMaster.logDebug("Tracking time: "+ time.getHours()+":" + time.getMinutes()+":"+time.getSeconds());
                    
                    
                    trackingPoints.add(trackingPoint);
                    dbTrackingPoint.insertTrackingPoint(trackingPoint); // insert tracking point to database
                    //RallyeMaster.logDebug("Count of Tracking Points: " + trackingPoints.size());
                    
                    // edit marker to Overlay
                    geoPoint = new GeoPoint((int)(trackingPoint.getLatitude()*1E6), (int)(trackingPoint.getLongitude()*1E6));
                    overlayItem = new OverlayItem(geoPoint,"trackedPoint", "this is a tracked point");
                    trackingMapOverlay.addOverlayItem(overlayItem);
                    
                    lastTrackingTime = currentTime;
                    mapView.getController().animateTo(geoPoint);
  
                }
            }
        }; 
        
		mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, trackingDistance, mLocationListener);
        mCurrentBestLocation = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
		
        
        //pause Button
        pauseButton = (Button) findViewById(R.id.pauseButton);
        pauseButton.setOnClickListener(new View.OnClickListener() {
			
			public void onClick(View v) {
				RallyeMaster.logDebug("Pause button pressed");
				mLocationManager.removeUpdates(mLocationListener);
				startButtonLayout.setVisibility(LinearLayout.VISIBLE); // makes the start button visible
				spButtonLayout.setVisibility(LinearLayout.GONE); // makes the stop an the pause button invisible
			}
		});
        
        //start Button 
        startButton = (Button) findViewById(R.id.startButton);
        startButton.setOnClickListener(new View.OnClickListener() {
			
			public void onClick(View v) {
				mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, trackingDistance, mLocationListener);
				startButtonLayout.setVisibility(LinearLayout.GONE); // makes the start button invisible
				spButtonLayout.setVisibility(LinearLayout.VISIBLE); // makes the stop an the pause button visible
				
			}
		});
    }

	
    @Override
	protected void onPause() {
    	mLocationManager.removeUpdates(mLocationListener);
		super.onPause();
	}
    
    


	@Override
	protected void onResume() {
		mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, trackingDistance, mLocationListener);
		super.onResume();
	}




	private static final int TWO_MINUTES = 1000 * 60 * 2;

    /** Determines whether one Location reading is better than the current Location fix
      * @param location  The new Location that you want to evaluate
      * @param currentBestLocation  The current Location fix, to which you want to compare the new one
      */
    protected boolean isBetterLocation(Location location, Location currentBestLocation) {
        if (currentBestLocation == null) {
            // A new location is always better than no location
            return true;
        }

        // Check whether the new location fix is newer or older
        long timeDelta = location.getTime() - currentBestLocation.getTime();
        boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
        boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
        boolean isNewer = timeDelta > 0;

        // If it's been more than two minutes since the current location, use the new location
        // because the user has likely moved
        if (isSignificantlyNewer) {
            return true;
        // If the new location is more than two minutes older, it must be worse
        } else if (isSignificantlyOlder) {
            return false;
        }

        // Check whether the new location fix is more or less accurate
        int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
        boolean isLessAccurate = accuracyDelta > 0;
        boolean isMoreAccurate = accuracyDelta < 0;
        boolean isSignificantlyLessAccurate = accuracyDelta > 200;

        // Check if the old and new location are from the same provider
        boolean isFromSameProvider = isSameProvider(location.getProvider(),
                currentBestLocation.getProvider());

        // Determine location quality using a combination of timeliness and accuracy
        if (isMoreAccurate) {
            return true;
        } else if (isNewer && !isLessAccurate) {
            return true;
        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
            return true;
        }
        return false;
    }

    /** Checks whether two providers are the same */
    private boolean isSameProvider(String provider1, String provider2) {
        if (provider1 == null) {
          return provider2 == null;
        }
        return provider1.equals(provider2);
    }
    
    /**
     * calculates the duration between two dates
     * @param startTime Date
     * @param endTime Date
     * @return duration in ms
     */
    private long getDuration(Date startTime, Date endTime){
    	long duration = endTime.getTime() - startTime.getTime();
    	return duration;
    }
    
    /**
     * Calculates the time in the format hh:mm:ss out of the time in ms
     * @param time time in ms
     * @return date
     */
    private Date getTrackingTime(long time){
    	Date date = new Date(time);
    	return date;
    }
      

	@Override
	public boolean onCreateOptionsMenu(Menu menu){
		MenuItem storeItem = menu.add(0,SHOW_TRACK,Menu.NONE, R.string.itemMenu_show);
		MenuItem newItem = menu.add(0,NEW_ITEM,Menu.NONE, R.string.itemMenu_new);
		MenuItem optionsItem = menu.add(0,OPTIONS_ITEM,Menu.NONE, R.string.itemMenu_options);
		
		storeItem.setIcon(android.R.drawable.ic_menu_info_details);
		newItem.setIcon(android.R.drawable.ic_menu_add); 
		optionsItem.setIcon(android.R.drawable.ic_menu_preferences);
		
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		super.onOptionsItemSelected(item);
		switch(item.getItemId()){
			case SHOW_TRACK:{
				Intent intent = new Intent(TrackingActivity.this, ShowTrackActivity.class); 
				int trackID = dbTrack.getLastInsertedTrackID();
				RallyeMaster.logDebug("Last inserted Track with id: "+ trackID);
				intent.putExtra("TrackID", trackID);
				startActivity(intent);
				return true;
			}
			case NEW_ITEM:{
				
				Intent intent = new Intent(this, ListTrackActivity.class);
				startActivity(intent);
				return true; 
			}
			case OPTIONS_ITEM:{
				Intent intent = new Intent(TrackingActivity.this, PreferencesActivity.class);
				startActivity(intent);
				return true;
			}
		}
		
		return false;
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * Updating preferences
	 */
	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
			String key) {
		// sets the new tracking distance
		if(key.equals(PREF_DISTANCE)){
			 trackingDistance = sharedPreferences.getInt(key, 0);
			 RallyeMaster.logDebug("TrackingDistance: " +  trackingDistance);
		}
		
	}
	

}
